using System;
using System.IO;
using System.Net.Sockets;
using xxpl1.Cert;
using xxpl1.HTTP.Parser;
using xxpl1.HTTP.Runtime;
using xxpl1.Log;
using xxpl1.Utils;

namespace xxpl1.Proxy
{
    public sealed class HttpClient : IDisposable
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(HttpClient));
        private readonly byte[] buffer = new byte[512];
        private Stream clientStream;
        private readonly Socket clientSocket;
        private readonly HttpRequestParser parser = new HttpRequestParser();

        public HttpClient(Socket clientSocket)
        {
            this.clientSocket = clientSocket;

            clientStream = new NetworkStream(clientSocket, true);
        }

        #region IDisposable Members

        public void Dispose()
        {
            clientSocket.Dispose();
            clientStream.Dispose();
        }

        #endregion

        public void Start()
        {
            clientStream.BeginRead(buffer, 0, buffer.Length, OnReceiveCompleted, clientStream);
        }

        private void OnReceiveCompleted(IAsyncResult ar)
        {
            try
            {
                int bytes = clientStream.EndRead(ar);

                if (bytes == 0)
                {
                    logger.Debug("Client DisConnected");
                    Dispose();
                    return;
                }

                parser.ParseReuest(buffer, 0, bytes);

                ProcessRequests();

                clientStream.BeginRead(buffer, 0, buffer.Length, OnReceiveCompleted, clientStream);
            }
            catch (Exception oe)
            {
                logger.Debug("Client Error", oe);
                Dispose();
            }
        }

        private void ProcessRequests()
        {
            while (parser.HasRequest())
            {
                HttpRequest request = parser.GetRequest();

                if ("CONNECT".Equals(request.Method, StringComparison.OrdinalIgnoreCase))
                {
                    SslHandShake(request);
                }
                else
                {
                    ProxyRuntime.Execute(request, new HttpResponse(clientStream));
                }
            }
        }

        private void SslHandShake(HttpRequest request)
        {
            clientStream.Write(Resources.Instance.SslHelloBytes, 0, Resources.Instance.SslHelloBytes.Length);

            clientStream.Flush();

            clientStream = SSPICertUtils.WrapStream(request.Headers["host"], clientStream);

            logger.InfoFormat("SSL Connect: {0}", request.Host);
        }
    }
}